:art: Kuaidi100

huangqimin001 3 年之前
父節點
當前提交
fe160a49f1

+ 68 - 1
api/maintenance_views.py

@@ -2,15 +2,18 @@
2 2
 
3 3
 from __future__ import division
4 4
 
5
+import json
6
+
5 7
 from django.conf import settings
6 8
 from django_logit import logit
7 9
 from django_response import response
8 10
 from paginator import pagination
9 11
 from TimeConvert import TimeConvert as tc
10 12
 
11
-from maintenance.models import MaintenaceInfo
13
+from maintenance.models import ExpressCompanyInfo, MaintenaceInfo
12 14
 from utils.admin_utils import is_admin, is_maintenanceman
13 15
 from utils.error.errno_utils import MaintenanceStatusCode
16
+from utils.kuaidi.synquery import KuaiDi100
14 17
 
15 18
 
16 19
 @logit
@@ -29,6 +32,11 @@ def maintenance_add(request):
29 32
     tracking_number = request.POST.get('tracking_number', '')
30 33
     maintenance_status = request.POST.get('maintenance_status', u'寄出运送中')
31 34
 
35
+    try:
36
+        company = ExpressCompanyInfo.objects.get(name=express_name, status=True)
37
+    except ExpressCompanyInfo.DoesNotExist:
38
+        company = None
39
+
32 40
     maintenance = MaintenaceInfo.objects.create(
33 41
         user_id=user_id,
34 42
         name=name,
@@ -41,6 +49,7 @@ def maintenance_add(request):
41 49
         point_id=point_id,
42 50
         point_name=point_name,
43 51
         express_name=express_name,
52
+        express_com=company.com if company else '',
44 53
         tracking_number=tracking_number,
45 54
         maintenance_status=maintenance_status,
46 55
         maintenance_status_at={maintenance_status: tc.utc_datetime()}
@@ -113,11 +122,21 @@ def maintenance_update(request):
113 122
     if point_name:
114 123
         maintenance.point_name = point_name
115 124
     if express_name:
125
+        try:
126
+            company = ExpressCompanyInfo.objects.get(name=express_name, status=True)
127
+        except ExpressCompanyInfo.DoesNotExist:
128
+            company = None
116 129
         maintenance.express_name = express_name
130
+        maintenance.express_com = company.com if company else ''
117 131
     if tracking_number:
118 132
         maintenance.tracking_number = tracking_number
119 133
     if back_express_name:
134
+        try:
135
+            company = ExpressCompanyInfo.objects.get(name=back_express_name, status=True)
136
+        except ExpressCompanyInfo.DoesNotExist:
137
+            company = None
120 138
         maintenance.back_express_name = back_express_name
139
+        maintenance.back_express_com = company.com if company else ''
121 140
     if back_tracking_number:
122 141
         maintenance.back_tracking_number = back_tracking_number
123 142
     if maintenance_status:
@@ -165,3 +184,51 @@ def maintenance_detail(request):
165 184
     return response(data={
166 185
         'maintenace': maintenance.data,
167 186
     })
187
+
188
+
189
+def is_tracking_signed(tracking_info):
190
+    if not tracking_info:
191
+        return False
192
+    items = tracking_info.get('data', [])
193
+    if not items:
194
+        return False
195
+    return items[0].get('status') == u'签收'
196
+
197
+
198
+@logit
199
+def maintenance_tracking_info(request):
200
+    maintenance_id = request.POST.get('maintenance_id', '')
201
+    type_ = request.POST.get('type', 'tracking')  # tracking / back_tracking
202
+
203
+    try:
204
+        maintenance = MaintenaceInfo.objects.get(id=maintenance_id, status=True)
205
+    except MaintenaceInfo.DoesNotExist:
206
+        return response(MaintenanceStatusCode.MAINTENACE_NOT_FOUND)
207
+
208
+    tracking_info = {}
209
+    if type_ == 'tracking':
210
+        if maintenance.express_com and maintenance.tracking_number:
211
+            tracking_info = KuaiDi100().track(maintenance.express_com, maintenance.tracking_number)
212
+    else:
213
+        if maintenance.back_express_com and maintenance.back_tracking_number:
214
+            tracking_info = KuaiDi100().track(maintenance.back_express_com, maintenance.back_tracking_number)
215
+
216
+    if tracking_info:
217
+        try:
218
+            tracking_info = json.loads(tracking_info)
219
+        except Exception:
220
+            tracking_info = {}
221
+
222
+    if tracking_info:
223
+        if type_ == 'tracking':
224
+            maintenance.tracking_info = tracking_info
225
+            maintenance.tracking_signed = is_tracking_signed(tracking_info)
226
+        else:
227
+            maintenance.back_tracking_info = tracking_info
228
+            maintenance.back_tracking_signed = is_tracking_signed(tracking_info)
229
+        maintenance.save()
230
+
231
+    return response(data={
232
+        'type': type_,
233
+        'tracking_info': tracking_info,
234
+    })

+ 2 - 0
api/urls.py

@@ -277,4 +277,6 @@ urlpatterns += [
277 277
 
278 278
     url(r'^admin/maintenance/update$', maintenance_views.maintenance_update, name='maintenance_update'),
279 279
     url(r'^admin/maintenance/list$', maintenance_views.maintenance_list, name='maintenance_list'),
280
+
281
+    url(r'^maintenance/tracking/info$', maintenance_views.maintenance_tracking_info, name='maintenance_tracking_info'),
280 282
 ]

+ 6 - 0
kodo/settings.py

@@ -419,6 +419,12 @@ KODO_DEFAULT_BRAND_DOMAIN = ''
419 419
 GIS_2_ADMINISTRATIVE_DIVISION = 'https://apis.map.qq.com/ws/geocoder/v1/?key=4FNBZ-TIIKW-MWJRQ-RHZJN-W6F7Q-BFBKX&location={0},{1}'
420 420
 PHONE_2_ADMINISTRATIVE_DIVISION = 'https://www.baifubao.com/callback?cmd=1059&callback=phone&phone={0}'
421 421
 
422
+# 快递100
423
+KUAIDI00 = {
424
+    'key': '',
425
+    'customer': '',
426
+}
427
+
422 428
 TESTING_SNS = ['000000']
423 429
 
424 430
 COMPONENT_CALLBACK_CONFIG = {

+ 2 - 2
maintenance/admin.py

@@ -10,11 +10,11 @@ class MaintenancePointInfoAdmin(admin.ModelAdmin):
10 10
 
11 11
 
12 12
 class ExpressCompanyInfoAdmin(admin.ModelAdmin):
13
-    list_display = ('name', 'status', 'created_at', 'updated_at')
13
+    list_display = ('name', 'com', 'status', 'created_at', 'updated_at')
14 14
 
15 15
 
16 16
 class MaintenanceInfoAdmin(admin.ModelAdmin):
17
-    list_display = ('user_id', 'name', 'phone', 'address', 'sn', 'desc', 'point_id', 'point_name', 'express_name', 'tracking_number', 'back_express_name', 'back_tracking_number', 'maintenance_status', 'status', 'created_at', 'updated_at')
17
+    list_display = ('user_id', 'name', 'phone', 'address', 'sn', 'desc', 'point_id', 'point_name', 'express_name', 'express_com', 'tracking_number', 'tracking_signed', 'back_express_name', 'back_express_com', 'back_tracking_number', 'back_tracking_signed', 'maintenance_status', 'status', 'created_at', 'updated_at')
18 18
 
19 19
 
20 20
 admin.site.register(MaintenacePointInfo, MaintenancePointInfoAdmin)

+ 26 - 0
maintenance/migrations/0008_auto_20210922_1646.py

@@ -0,0 +1,26 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.29 on 2021-09-22 08:46
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations
6
+import jsonfield.fields
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    dependencies = [
12
+        ('maintenance', '0007_auto_20210922_1522'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.AddField(
17
+            model_name='maintenaceinfo',
18
+            name='back_tracking_info',
19
+            field=jsonfield.fields.JSONField(default={}, help_text='\u5bc4\u56de\u5feb\u9012\u4fe1\u606f', verbose_name='back_tracking_info'),
20
+        ),
21
+        migrations.AddField(
22
+            model_name='maintenaceinfo',
23
+            name='tracking_info',
24
+            field=jsonfield.fields.JSONField(default={}, help_text='\u5feb\u9012\u4fe1\u606f', verbose_name='tracking_info'),
25
+        ),
26
+    ]

+ 20 - 0
maintenance/migrations/0009_expresscompanyinfo_com.py

@@ -0,0 +1,20 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.29 on 2021-09-22 09:00
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('maintenance', '0008_auto_20210922_1646'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='expresscompanyinfo',
17
+            name='com',
18
+            field=models.CharField(blank=True, help_text='\u7f16\u7801', max_length=255, null=True, verbose_name='com'),
19
+        ),
20
+    ]

+ 25 - 0
maintenance/migrations/0010_auto_20210922_1704.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.29 on 2021-09-22 09:04
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('maintenance', '0009_expresscompanyinfo_com'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='maintenaceinfo',
17
+            name='back_express_com',
18
+            field=models.CharField(blank=True, help_text='\u5bc4\u56de\u5feb\u9012\u7f16\u7801', max_length=255, null=True, verbose_name='back_express_com'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='maintenaceinfo',
22
+            name='express_com',
23
+            field=models.CharField(blank=True, help_text='\u5feb\u9012\u7f16\u7801', max_length=255, null=True, verbose_name='express_com'),
24
+        ),
25
+    ]

+ 41 - 0
maintenance/migrations/0011_auto_20210922_1727.py

@@ -0,0 +1,41 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.29 on 2021-09-22 09:27
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+import jsonfield.fields
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    dependencies = [
12
+        ('maintenance', '0010_auto_20210922_1704'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.AddField(
17
+            model_name='maintenaceinfo',
18
+            name='back_tracking_signed',
19
+            field=models.BooleanField(default=False, help_text='\u5bc4\u56de\u5feb\u9012\u662f\u5426\u5df2\u7b7e\u6536', verbose_name='back_tracking_signed'),
20
+        ),
21
+        migrations.AddField(
22
+            model_name='maintenaceinfo',
23
+            name='tracking_signed',
24
+            field=models.BooleanField(default=False, help_text='\u5feb\u9012\u662f\u5426\u5df2\u7b7e\u6536', verbose_name='tracking_signed'),
25
+        ),
26
+        migrations.AlterField(
27
+            model_name='maintenaceinfo',
28
+            name='back_tracking_info',
29
+            field=jsonfield.fields.JSONField(blank=True, default={}, help_text='\u5bc4\u56de\u5feb\u9012\u4fe1\u606f', null=True, verbose_name='back_tracking_info'),
30
+        ),
31
+        migrations.AlterField(
32
+            model_name='maintenaceinfo',
33
+            name='maintenance_status_at',
34
+            field=jsonfield.fields.JSONField(blank=True, default={}, help_text='\u7ef4\u4fee\u72b6\u6001\u53d8\u66f4\u65f6\u95f4', null=True, verbose_name='maintenance_status_at'),
35
+        ),
36
+        migrations.AlterField(
37
+            model_name='maintenaceinfo',
38
+            name='tracking_info',
39
+            field=jsonfield.fields.JSONField(blank=True, default={}, help_text='\u5feb\u9012\u4fe1\u606f', null=True, verbose_name='tracking_info'),
40
+        ),
41
+    ]

+ 15 - 1
maintenance/models.py

@@ -35,6 +35,7 @@ class MaintenacePointInfo(BaseModelMixin):
35 35
 
36 36
 class ExpressCompanyInfo(BaseModelMixin):
37 37
     name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'名称')
38
+    com = models.CharField(_(u'com'), max_length=255, blank=True, null=True, help_text=u'编码')
38 39
 
39 40
     class Meta:
40 41
         verbose_name = _(u'快递公司信息')
@@ -48,6 +49,7 @@ class ExpressCompanyInfo(BaseModelMixin):
48 49
         return {
49 50
             'id': self.pk,
50 51
             'name': self.name,
52
+            'com': self.com,
51 53
         }
52 54
 
53 55
 
@@ -67,14 +69,20 @@ class MaintenaceInfo(BaseModelMixin):
67 69
     point_name = models.CharField(_(u'point_name'), max_length=255, blank=True, null=True, help_text=u'维修点名称')
68 70
 
69 71
     express_name = models.CharField(_(u'express_name'), max_length=255, blank=True, null=True, help_text=u'快递公司')
72
+    express_com = models.CharField(_(u'express_com'), max_length=255, blank=True, null=True, help_text=u'快递编码')
70 73
     tracking_number = models.CharField(_(u'tracking_number'), max_length=255, blank=True, null=True, help_text=u'快递单号')
74
+    tracking_info = JSONField(_(u'tracking_info'), blank=True, null=True, default={}, help_text=u'快递信息')
75
+    tracking_signed = models.BooleanField(_(u'tracking_signed'), default=False, help_text=u'快递是否已签收')
71 76
 
72 77
     back_express_name = models.CharField(_(u'back_express_name'), max_length=255, blank=True, null=True, help_text=u'寄回快递公司')
78
+    back_express_com = models.CharField(_(u'back_express_com'), max_length=255, blank=True, null=True, help_text=u'寄回快递编码')
73 79
     back_tracking_number = models.CharField(_(u'back_tracking_number'), max_length=255, blank=True, null=True, help_text=u'寄回快递单号')
80
+    back_tracking_info = JSONField(_(u'back_tracking_info'), blank=True, null=True, default={}, help_text=u'寄回快递信息')
81
+    back_tracking_signed = models.BooleanField(_(u'back_tracking_signed'), default=False, help_text=u'寄回快递是否已签收')
74 82
 
75 83
     # 寄出运送中、已签收修理中、已修复寄回、寄回签收
76 84
     maintenance_status = models.CharField(_(u'maintenance_status'), max_length=8, default=u'寄出运送中', help_text=u'维修状态')
77
-    maintenance_status_at = JSONField(_(u'maintenance_status_at'), default={}, help_text=u'维修状态变更时间')
85
+    maintenance_status_at = JSONField(_(u'maintenance_status_at'), blank=True, null=True, default={}, help_text=u'维修状态变更时间')
78 86
 
79 87
     class Meta:
80 88
         verbose_name = _(u'维修信息')
@@ -104,9 +112,15 @@ class MaintenaceInfo(BaseModelMixin):
104 112
             'point_id': self.point_id,
105 113
             'point_name': self.point_name,
106 114
             'express_name': self.express_name,
115
+            'express_com': self.express_com,
107 116
             'tracking_number': self.tracking_number,
117
+            'tracking_info': self.tracking_info,
118
+            'tracking_signed': self.tracking_signed,
108 119
             'back_express_name': self.back_express_name,
120
+            'back_express_com': self.back_express_com,
109 121
             'back_tracking_number': self.back_tracking_number,
122
+            'back_tracking_info': self.back_tracking_info,
123
+            'back_tracking_signed': self.back_tracking_signed,
110 124
             'maintenance_status': self.maintenance_status,
111 125
             'maintenance_status_at': {k: tc.local_string(utc_dt=tc.string_to_utc_datetime(v, format='%Y-%m-%dT%H:%M:%S.%fZ')) for k, v in self.maintenance_status_at.items()},
112 126
             'created_at': tc.local_string(utc_dt=self.created_at),

+ 0 - 0
utils/kuaidi/__init__.py


+ 22 - 0
utils/kuaidi/autonumber.py

@@ -0,0 +1,22 @@
1
+# -*- coding: utf-8 -*-'
2
+
3
+import requests
4
+
5
+
6
+class KuaiDi100:
7
+    def __init__(self):
8
+        self.key = ''  # TODO 客户授权key
9
+        self.url = 'https://www.kuaidi100.com/autonumber/auto'  # 请求地址
10
+
11
+    def auto_number(self, num):
12
+        """
13
+        智能单号识别
14
+        :param num: 快递单号
15
+        :return: requests.Response.text
16
+        """
17
+        req_params = {'key': self.key, 'num': num}
18
+        return requests.post(self.url, req_params).text  # 发送请求
19
+
20
+
21
+result = KuaiDi100().auto_number('YT9693083639795')
22
+print(result)

+ 103 - 0
utils/kuaidi/billparcels.py

@@ -0,0 +1,103 @@
1
+# -*- coding: utf-8 -*-'
2
+
3
+import hashlib
4
+import json
5
+import time
6
+
7
+import requests
8
+
9
+
10
+class KuaiDi100:
11
+    def __init__(self):
12
+        self.key = ''  # TODO 客户授权key
13
+        self.secret = ''  # TODO 电子面单secret
14
+        self.url = 'https://poll.kuaidi100.com/print/billparcels.do'  # 请求地址
15
+
16
+    def submit(self, param, settings):
17
+        """
18
+        发货单打印
19
+        :param param: 模板配置信息和自定义参数信息
20
+        :param settings: 纸张配置信息
21
+        :return: requests.Response.text
22
+        """
23
+        timestamp = str(time.time())
24
+        md = hashlib.md5()
25
+        param_str = json.dumps(param)
26
+        temp_sign = param_str + timestamp + self.key + self.secret
27
+        md.update(temp_sign.encode())
28
+        sign = md.hexdigest().upper()
29
+        req_params = {
30
+            'method': 'billparcels',
31
+            'key': self.key,
32
+            't': timestamp,
33
+            'sign': sign,
34
+            'param': param_str,
35
+            'settings': json.dumps(settings)
36
+        }
37
+        return requests.post(self.url, req_params).text  # 发送请求
38
+
39
+
40
+param = {
41
+    "tempid": "xxxx",  # 模板编码,通过管理后台的打印发货单模板配置信息获取
42
+    "siid": "xxxx",  # 打印设备码,通过打印机输出的设备码进行获取
43
+    "callBackUrl": "https://www.baidu.com/fhd/callback",  # 打印状态对调地址
44
+    "petName": "kd100",  # 自定义参数
45
+    "recName": "小百",  # 自定义参数
46
+    "recPhone": "10086",  # 自定义参数
47
+    "payTime": "2021-01-15 15:40:55",  # 自定义参数
48
+    "expressName": "德邦快递",  # 自定义参数
49
+    "printTime": "2021-01-15 15:41:30",  # 自定义参数
50
+    "printCount": "1",  # 自定义参数
51
+    "address": "广东省深圳市南山区金蝶软件园",  # 自定义参数
52
+    "total": "21", "remark": "购物小票作为购物凭证,请妥善保管,您有任何疑问,请咨询服务热线 123456798",  # 自定义参数
53
+    "img0": {  # 图片参数,多图片时用img0,img1,img2等追加
54
+        "type": "code_128",
55
+        "content": "887921256577",
56
+        "width": 350,
57
+        "height": 100
58
+    },
59
+    "tab0": [  # 表格参数,多表格时用tab0,tab1,tab2等追加对象
60
+        {
61
+            "prodName": "热敏纸",
62
+            "count": "5",
63
+            "specs": "76*130",
64
+            "unitPrice": "30",
65
+            "price": "150"
66
+        },
67
+        {
68
+            "prodName": "热敏纸",
69
+            "count": "10",
70
+            "specs": "100*180",
71
+            "unitPrice": "50",
72
+            "price": "500"
73
+        },
74
+        {
75
+            "prodName": "续打纸",
76
+            "count": "5",
77
+            "specs": "",
78
+            "unitPrice": "40",
79
+            "price": "200"
80
+        },
81
+        {
82
+            "prodName": "云打印机",
83
+            "count": "1",
84
+            "specs": "二代",
85
+            "unitPrice": "499",
86
+            "price": "499"
87
+        }
88
+    ]
89
+}
90
+
91
+settings = {
92
+    "pageWidth": 100,  # 纸张宽,单位mm,默认值:100
93
+    "pageHeight": 180,  # 纸张高,单位mm ,续打纸张时,该字段设置为null或空串
94
+    "margins": {  # 边距
95
+        "top": 5,  # 上边距,单位:mm,默认:0
96
+        "bottom": 5,  # 下边距,单位:mm,默认:0
97
+        "left": 5,  # 左边距,单位:mm,默认:0
98
+        "right": 5  # 右边距,单位:mm,默认:0
99
+    }
100
+}
101
+
102
+result = KuaiDi100().submit(param, settings)
103
+print(result)

+ 48 - 0
utils/kuaidi/maptrack.py

@@ -0,0 +1,48 @@
1
+# -*- coding: utf-8 -*-'
2
+
3
+import hashlib
4
+import json
5
+
6
+import requests
7
+
8
+
9
+class KuaiDi100:
10
+    def __init__(self):
11
+        self.key = ''  # TODO 客户授权key
12
+        self.customer = ''  # TODO 查询公司编号
13
+        self.url = 'https://poll.kuaidi100.com/poll/maptrack.do'  # 请求地址
14
+
15
+    def map_track(self, com, num, phone, ship_from, ship_to, orderTime):
16
+        """
17
+        快递查询地图轨迹
18
+        :param com: 查询的快递公司的编码,一律用小写字母
19
+        :param num: 查询的快递单号,单号的最大长度是32个字符
20
+        :param phone: 收件人或寄件人的手机号或固话(也可以填写后四位,如果是固话,请不要上传分机号)
21
+        :param ship_from: 出发地城市,省-市-区,非必填,填了有助于提升签收状态的判断的准确率,请尽量提供
22
+        :param ship_to: 目的地城市,省-市-区,非必填,填了有助于提升签收状态的判断的准确率,且到达目的地后会加大监控频率,请尽量提供
23
+        :param orderTime: 订单下单时间,格式为(yyyy-MM-dd HH:mm:ss)如:2020-12-16 12:59:59
24
+        :return: requests.Response.text
25
+        """
26
+        param = {
27
+            'com': com,
28
+            'num': num,
29
+            'phone': phone,
30
+            'from': ship_from,
31
+            'to': ship_to,
32
+            'show': '0',  # 返回数据格式。0:json(默认),1:xml,2:html,3:text
33
+            'order': 'desc',  # 返回结果排序方式。desc:降序(默认),asc:升序
34
+            'orderTime': orderTime
35
+        }
36
+        param_str = json.dumps(param)  # 转json字符串
37
+
38
+        # 签名加密, 用于验证身份, 按param + key + customer 的顺序进行MD5加密(注意加密后字符串要转大写), 不需要“+”号
39
+        temp_sign = param_str + self.key + self.customer
40
+        md = hashlib.md5()
41
+        md.update(temp_sign.encode())
42
+        sign = md.hexdigest().upper()
43
+        request_data = {'customer': self.customer, 'param': param_str, 'sign': sign}
44
+        return requests.post(self.url, request_data).text  # 发送请求
45
+
46
+
47
+result = KuaiDi100().map_track('yuantong', 'YT9693083639795', '', '广东省江门市', '广东省深圳市', '2021-08-01 20:04:44')
48
+print(result)

+ 48 - 0
utils/kuaidi/sms.py

@@ -0,0 +1,48 @@
1
+# -*- coding: utf-8 -*-'
2
+
3
+import hashlib
4
+
5
+import requests
6
+
7
+
8
+class KuaiDi100:
9
+    def __init__(self):
10
+        self.key = ''  # TODO 客户授权key
11
+        self.userid = ''  # TODO 查询公司编号
12
+        md = hashlib.md5()
13
+        md.update((self.key + self.userid).encode())
14
+        self.sign = md.hexdigest().upper()
15
+        self.url = 'https://apisms.kuaidi100.com/sms/send.do'  # 接口地址,或者 http://apisms.kuaidi100.com:9502/sms/send.do
16
+
17
+    def send_sms(self, seller, phone, tid, sms_content, order_id):
18
+        """
19
+        短信发送
20
+        :param seller: 商户名称签名;最好用简称,该字段信息会在短信标签处显示。不要超过5个字符
21
+        :param phone: 接收短信手机号
22
+        :param tid: 短信模板ID
23
+        :param sms_content: 短信模板替换内容
24
+        :param order_id: 外部订单号:当该短信发送模板有回调地址时,外部订单号会返回给调用者,方便用户更新数据
25
+        :return: requests.Response.text
26
+        """
27
+        req_params = {
28
+            'sign': self.sign,  # 加密签名信息:MD5(key + userid);加密后字符串转大写
29
+            'userid': self.userid,  # 我方分配给贵司的的短信接口用户ID,点击查看账号信息
30
+            'seller': seller,  # 商户名称签名;最好用简称,该字段信息会在短信标签处显示。不要超过5个字符
31
+            'phone': phone,  # 接收短信手机号
32
+            'tid': tid,  # 短信模板ID
33
+            'content': sms_content,  # 短信模板替换内容
34
+            'outorder': order_id,  # 外部订单号:当该短信发送模板有回调地址时,外部订单号会返回给调用者,方便用户更新数据
35
+            # 'callback': 'https//www.baidu.com/kd100/callback'  # 回调地址:如果客户在发送短信时填写该参数,将按照这个参数回调短信发送状态;如果为空,将按照模板配置的地址回调短信发送状态;如果两个参数都不填写,将不会回调通知状态
36
+        }
37
+
38
+        return requests.post(self.url, req_params).text  # 发送请求
39
+
40
+
41
+content = {  # 短信模板替换内容
42
+    "接收人姓名": "小百",
43
+    "公司名": "快递100",
44
+    "快递单号": "154893238584",
45
+    "url": "https://www.kuaidi100.com"
46
+}
47
+result = KuaiDi100().send_sms('快递100', '13800138000', '11', content, '123456789')
48
+print(result)

+ 52 - 0
utils/kuaidi/subscribe.py

@@ -0,0 +1,52 @@
1
+# -*- coding: utf-8 -*-'
2
+
3
+import json
4
+
5
+import requests
6
+
7
+
8
+class KuaiDi100:
9
+    def __init__(self):
10
+        self.key = ''  # TODO 客户授权key
11
+        self.url = 'https://poll.kuaidi100.com/poll'  # 请求地址
12
+
13
+    def submit(self, com, num, phone, ship_from, ship_to):
14
+        """
15
+        物流轨迹订阅
16
+        :param com: 快递公司编码
17
+        :param num: 快递单号
18
+        :param phone: 收件人或寄件人的手机号或固话(也可以填写后四位,如果是固话,请不要上传分机号)
19
+        :param ship_from: 出发地城市,省-市-区,非必填,填了有助于提升签收状态的判断的准确率,请尽量提供
20
+        :param ship_to: 目的地城市,省-市-区,非必填,填了有助于提升签收状态的判断的准确率,且到达目的地后会加大监控频率,请尽量提供
21
+        :return: requests.Response.text
22
+        """
23
+        param = {
24
+            'company': com,
25
+            'number': num,
26
+            'from': ship_from,
27
+            'to': ship_to,
28
+            'key': self.key,
29
+            'parameters': {
30
+                'callbackurl': 'https://www.baidu.com/kd100/callback',  # 回调接口的地址。如果需要在推送信息回传自己业务参数,可以在回调地址URL后面拼接上去,例如:https://www.baidu.com/kd100/callback?orderId=123
31
+                'salt': None,  # 签名用随机字符串。32位自定义字符串。添加该参数,则推送的时候会增加sign给贵司校验消息的可靠性
32
+                'resultv2': '1',  # 添加此字段表示开通行政区域解析功能。0:关闭(默认),1:开通行政区域解析功能
33
+                'autoCom': '0',  # 添加此字段且将此值设为1,则表示开始智能判断单号所属公司的功能,开启后,company字段可为空,即只传运单号(number字段),我方收到后会根据单号判断出其所属的快递公司(即company字段)。建议只有在无法知道单号对应的快递公司(即company的值)的情况下才开启此功能
34
+                'interCom': '0',  # 添加此字段且将此值设为1,则表示开启国际版,开启后,若订阅的单号(即number字段)属于国际单号,会返回出发国与目的国两个国家的跟踪信息,本功能暂时只支持邮政体系(国际类的邮政小包、EMS)内的快递公司,若单号我方识别为非国际单,即使添加本字段,也不会返回destResult元素组
35
+                'departureCountry': '',  # 出发国家编码,interCom=1的国际单号最好提供该值
36
+                'departureCom': '',  # 出发国家快递公司的编码,interCom=1的国际单号最好提供该值
37
+                'destinationCountry': '',  # 目的国家编码,interCom=1的国际单号最好提供该值
38
+                'destinationCom': '',  # 目的国家快递公司的编码,interCom=1的国际单号最好提供该值
39
+                'phone': phone
40
+            }
41
+        }
42
+
43
+        req_params = {
44
+            'schema': 'json',  # 查询公司编号
45
+            'param': json.dumps(param)  # 参数数据
46
+        }
47
+
48
+        return requests.post(self.url, req_params).text  # 发送请求
49
+
50
+
51
+result = KuaiDi100().submit('yuantong', 'YT9693083639795', '', '江门市', '深圳市')
52
+print(result)

+ 48 - 0
utils/kuaidi/synquery.py

@@ -0,0 +1,48 @@
1
+# -*- coding: utf-8 -*-'
2
+
3
+import hashlib
4
+import json
5
+
6
+import requests
7
+from django.conf import settings
8
+
9
+
10
+class KuaiDi100:
11
+    def __init__(self):
12
+        self.key = settings.KUAIDI00.get('key', '')  # TODO 客户授权key
13
+        self.customer = settings.KUAIDI00.get('customer', '')  # TODO 查询公司编号
14
+        self.url = 'https://poll.kuaidi100.com/poll/query.do'  # 请求地址
15
+
16
+    def track(self, com, num, phone=None, ship_from=None, ship_to=None):
17
+        """
18
+        物流轨迹实时查询: https://api.kuaidi100.com/document/5f0ffb5ebc8da837cbd8aefc.html
19
+        :param com: 查询的快递公司的编码,一律用小写字母
20
+        :param num: 查询的快递单号,单号的最大长度是32个字符
21
+        :param phone: 收件人或寄件人的手机号或固话(也可以填写后四位,如果是固话,请不要上传分机号)
22
+        :param ship_from: 出发地城市,省-市-区,非必填,填了有助于提升签收状态的判断的准确率,请尽量提供
23
+        :param ship_to: 目的地城市,省-市-区,非必填,填了有助于提升签收状态的判断的准确率,且到达目的地后会加大监控频率,请尽量提供
24
+        :return: requests.Response.text
25
+        """
26
+        param = {
27
+            'com': com,
28
+            'num': num,
29
+            'phone': phone,
30
+            'from': ship_from,
31
+            'to': ship_to,
32
+            'resultv2': '1',  # 添加此字段表示开通行政区域解析功能。0:关闭(默认),1:开通行政区域解析功能,2:开通行政解析功能并且返回出发、目的及当前城市信息
33
+            'show': '0',  # 返回数据格式。0:json(默认),1:xml,2:html,3:text
34
+            'order': 'desc'  # 返回结果排序方式。desc:降序(默认),asc:升序
35
+        }
36
+        param_str = json.dumps(param)  # 转json字符串
37
+
38
+        # 签名加密, 用于验证身份, 按param + key + customer 的顺序进行MD5加密(注意加密后字符串要转大写), 不需要“+”号
39
+        temp_sign = param_str + self.key + self.customer
40
+        md = hashlib.md5()
41
+        md.update(temp_sign.encode())
42
+        sign = md.hexdigest().upper()
43
+        request_data = {'customer': self.customer, 'param': param_str, 'sign': sign}
44
+        return requests.post(self.url, request_data).text  # 发送请求
45
+
46
+
47
+# result = KuaiDi100().track('yuantong', 'YT9693083639795', '', '广东省江门市', '广东省深圳市')
48
+# print(result)